#ifndef __I_BUFFER_H__
#define __I_BUFFER_H__

#include <zlib/lib/I_Ptr.h>
#include <stdint.h>
#include <stdlib.h>

//128 bytes
#define DEFAULT_BUFFER_BASE_SIZE 	128
//default IOBufferData 1k
#define DEFAULT_BUFFER_INDEX		3
//default MIOBuffer water mark
#define DEFAULT_WATER_MARK			1024
//one oepr append max blocks number
#define MAX_APPEND_BLOCKS			3
#define MAX_READER_NUMBER			5

#define BUFFER_SIZE_INDEX_128		0
#define BUFFER_SIZE_INDEX_256		1
#define BUFFER_SIZE_INDEX_512		2
#define BUFFER_SIZE_INDEX_1K		3
#define BUFFER_SIZE_INDEX_2K		4
#define BUFFER_SIZE_INDEX_4K		5
#define BUFFER_SIZE_INDEX_8K		6
#define BUFFER_SIZE_INDEX_16K		7
#define BUFFER_SIZE_INDEX_32K		8
#define BUFFER_SIZE_INDEX_64K		9
#define BUFFER_SIZE_INDEX_128K		10
#define BUFFER_SIZE_INDEX_256K		11
#define BUFFER_SIZE_INDEX_512K		12
#define BUFFER_SIZE_INDEX_1M		13
#define BUFFER_SIZE_INDEX_2M		14
#define MAX_BUFFER_SIZE_INDEX		14

#define BUFFER_SIZE_FOR_INDEX(_i) (DEFAULT_BUFFER_BASE_SIZE *(1 << (_i)))

class MIOBuffer;

class IOBufferData : public RefCountObj
{
public:
	IOBufferData();

	void alloc(int size_index);
	void dealloc();

	int64_t block_size();

	char *data()
	{
		return _data;
	}

	operator char *()
	{
		return _data;
	}

	virtual void free();

	char *_data;
private:
	int _size_index;

	IOBufferData(const IOBufferData &);
	IOBufferData &operator=(const IOBufferData &);
};

class IOBufferBlock : public RefCountObj
{
public:
	IOBufferBlock();

	void alloc(int size_index);
	void dealloc();
	void free();

	char *buff()
	{
		return data->_data;
	}

	char *start()
	{
		return _start;
	}

	char *end()
	{
		return _end;
	}

	int64_t read_alive()
	{
		return (int64_t)(_end - _start);
	}

	int64_t write_alive()
	{
		return (int64_t)(_buff_end - _end);
	}

	int64_t block_size()
	{
		return data->block_size();
	}

	void fill(int64_t len);
	void consum(int64_t len);
	void reset();

	//only clone IOBufferBlock base data, not create IOBufferData
	//suggest use this function replace of memcpy function
	//it can lessens value copy cause capability consume
	IOBufferBlock *clone();

	Ptr<IOBufferBlock> next;
	Ptr<IOBufferData> data;

	char *_start;
	char *_end;
	char *_buff_end;
private:
	IOBufferBlock(const IOBufferBlock &);
	IOBufferBlock &operator=(const IOBufferBlock &);
};


class IOBufferReader
{
public:
	IOBufferReader();

	int64_t read_alive();
	char *start();
	IOBufferReader *clone();

	int64_t block_read_alive();
	int64_t read(void *buff, int64_t len);
	void consum(int64_t len);

	void skip_empty_block();

	MIOBuffer *allocater;
	bool allocated();

	void dealloc();

	Ptr<IOBufferBlock> block;
	int64_t start_offset;
};

class MIOBuffer
{
public:
	//the size_index is create IOBufferData block size
	//the water_mark indicate create IOBufferBlock when the read_alive < water_mark and current_write_alive < water_mark
	MIOBuffer(int size_index = DEFAULT_BUFFER_INDEX, int64_t water_mark = DEFAULT_WATER_MARK, bool empty = false);
	~MIOBuffer();

	bool empty()
	{
		return !_writer;
	}

	//only append empty IOBufferBlock (block->read_alive <= 0)
	void append_block(IOBufferBlock *block);
	//only copy IOBufferBlock witch data (block->read_alive > 0)
	void copy_block(IOBufferBlock *block);
	//if read_alive < water_mark and current_write_alive < water_mark, append new empty block.
	void check_add_block();

	int64_t read_alive();
	int64_t current_write_alive();
	//the write_alive will be check add block
	int64_t write_alive();

	//direct operate IOBufferBlock
	//get first IOBufferBlock, write or read in IOBufferBlock and fill or consum data.
	//usually we read or write in fd to IOBufferData.
	//we should supply operate base IOBufferData interface.
	IOBufferBlock *first_write_block();
	void fill(int64_t len);

	//copy buff data to MIOBuffer and fill data_len in MIOBuffer.
	//the write function will create IOBufferData so that hold data
	//this function has not traffic control
	int64_t write(const void *buff, int64_t data_len);

	//reader
	void init_readers();
	IOBufferReader *alloc_reader();
	void dealloc_readers();
	void dealloc_reader(IOBufferReader *reader);
	IOBufferReader *clone_reader(IOBufferReader *reader);

	int64_t _water_mark;
	int64_t _size_index;

	IOBufferReader readers[MAX_READER_NUMBER];

	Ptr<IOBufferBlock> _writer;
};


IOBufferData *new_IOBufferData(int size_index);
IOBufferBlock *new_IOBufferBlock(int size_index);

#endif //__I_BUFFER_H__
